﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using NUnit.Framework;
using System.Collections.Specialized;
using System.Collections;

namespace SRTSolutions.Elevate.Guide
{
    public class ObservableDictionary
    {
        #region Unit Tests

        ObservableDictionary<string, int> underTest;

        [SetUp]
        public void Setup()
        {
            underTest = new ObservableDictionary<string, int>();
            underTest.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(underTest_CollectionChanged);
            eventFired = false;
            action = NotifyCollectionChangedAction.Reset;
        }

        #region Add
        [Test]
        public void AddKeyAndValueToDictionaryShouldFireCollectionChanged()
        {
            underTest.Add("SRT", 1);
            CheckThatAddHappened();
            CheckNewItems();
        }

        [Test]
        public void AddObjectAndObjectToDictionaryShouldFireCollectionChanged()
        {
            ((IDictionary)underTest).Add((object)"SRT", (object)1);
            CheckThatAddHappened();
            CheckNewUntypedItems();
        }

        [Test]
        public void AddKeyValuePairToDictionaryShouldFireCollectionChanged()
        {
            ((ICollection<KeyValuePair<string, int>>)underTest).Add(new KeyValuePair<string, int>("SRT", 1));
            CheckThatAddHappened();
            CheckNewItems();
        }

        private void CheckThatAddHappened()
        {
            Assert.IsTrue(eventFired);
            Assert.AreEqual(NotifyCollectionChangedAction.Add, action);
            Assert.AreEqual(1, underTest.Count);
            Assert.IsTrue(oldItems == null);
            Assert.IsTrue(newItems != null);
        }

        private void CheckNewItems()
        {
            var typedNewItems = newItems.Cast<KeyValuePair<string, int>>();
            Assert.AreEqual(1, typedNewItems.Count());
            Assert.AreEqual("SRT", typedNewItems.ElementAt(0).Key);
            Assert.AreEqual(1, typedNewItems.ElementAt(0).Value);
        }

        private void CheckNewUntypedItems()
        {
            var typedNewItems = newItems.Cast<KeyValuePair<object, object>>();
            Assert.AreEqual(1, typedNewItems.Count());
            Assert.AreEqual("SRT", (string)typedNewItems.ElementAt(0).Key);
            Assert.AreEqual(1, (int)typedNewItems.ElementAt(0).Value);
        }
        #endregion

        #region Remove
        private void SetupRemove()
        {
            underTest.Add("SRT", 1);
            Assert.AreEqual(1, underTest.Count);
            eventFired = false;
        }

        [Test]
        public void RemoveTypedKeyShouldFireCollectionChanged()
        {
            SetupRemove();
            underTest.Remove("SRT");
            CheckThatRemoveHappened();
            CheckRemovedItems();
        }

        [Test]
        public void RemoveUntypedKeyShouldFireCollectionChanged()
        {
            SetupRemove();
            ((IDictionary)underTest).Remove((object)"SRT");
            CheckThatRemoveHappened();
            CheckUntypedRemovedItems();
        }

        [Test]
        public void RemoveKeyValuePairShouldFireCollectionChanged()
        {
            SetupRemove();
            ((ICollection<KeyValuePair<string, int>>)underTest).Remove(new KeyValuePair<string, int>("SRT", 1));
            CheckThatRemoveHappened();
            CheckRemovedItems();
        }

        private void CheckThatRemoveHappened()
        {
            Assert.IsTrue(eventFired);
            Assert.AreEqual(NotifyCollectionChangedAction.Remove, action);
            Assert.AreEqual(0, underTest.Count);
            Assert.IsTrue(oldItems != null);
            Assert.IsTrue(newItems == null);
        }

        private void CheckRemovedItems()
        {
            var typedOldItems = oldItems.Cast<KeyValuePair<string, int>>();
            Assert.AreEqual(1, typedOldItems.Count());
            Assert.AreEqual("SRT", typedOldItems.ElementAt(0).Key);
            Assert.AreEqual(1, typedOldItems.ElementAt(0).Value);
        }

        private void CheckUntypedRemovedItems()
        {
            var typedOldItems = oldItems.Cast<KeyValuePair<object, object>>();
            Assert.AreEqual(1, typedOldItems.Count());
            Assert.AreEqual("SRT", (string)typedOldItems.ElementAt(0).Key);
            Assert.AreEqual(1, (int)typedOldItems.ElementAt(0).Value);
        }

        [Test]
        public void RemoveTypedKeyDoesNothingIfKeyIsNotInDictionary()
        {
            SetupRemove();
            underTest.Remove("Boeing");
            CheckThatRemoveDidNotHappen();
        }

        [Test]
        public void RemoveUntypedKeyDoesNothingIfKeyIsNotInDictionary()
        {
            SetupRemove();
            ((IDictionary)underTest).Remove((object)"Boeing");
            CheckThatRemoveDidNotHappen();
        }

        [Test]
        public void RemoveKeyValuePairDoesNothingIfKeyIsNotInDictionary()
        {
            SetupRemove();
            ((ICollection<KeyValuePair<string, int>>)underTest).Remove(new KeyValuePair<string, int>("Boeing", 1));
            CheckThatRemoveDidNotHappen();
        }

        private void CheckThatRemoveDidNotHappen()
        {
            Assert.IsFalse(eventFired);
            Assert.AreEqual(1, underTest.Count);
        }
        #endregion

        #region Clear
        [Test]
        public void ClearShouldFireCollectionChangedEvent()
        {
            SetupRemove();
            underTest.Clear();
            CheckThatRemoveHappened();
        }
        #endregion

        #region Modify
        [Test]
        public void ModifyShouldFireCollectionChangedEvent()
        {
            SetupRemove();
            underTest["SRT"] = 2;
            CheckThatModifyHappened();
        }

        [Test]
        public void ModifyUntypedShouldFireCollectionChangedEvent()
        {
            SetupRemove();
            ((IDictionary)underTest)[(object)("SRT")] = (object)2;
            CheckThatModifyHappened();
        }

        private void CheckThatModifyHappened()
        {
            Assert.IsTrue(eventFired);
            Assert.AreEqual(NotifyCollectionChangedAction.Replace, action);
            Assert.AreEqual(1, underTest.Count);
            Assert.IsTrue(oldItems != null);
            Assert.IsTrue(newItems != null);
        }
        #endregion

        private bool eventFired = false;
        private NotifyCollectionChangedAction action;
        private IList newItems;
        private IList oldItems;
        void underTest_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            eventFired = true;
            action = e.Action;
            newItems = e.NewItems;
            oldItems = e.OldItems;
        }

        #endregion
    }
}
